import numpy as np
'''
NumPy 是 Python 中一个基本的科学计算库，包含以下特性：

强大的 N 维数组对象；
精巧的广播（broadcasting）功能；
C/C++ 和 Fortran 代码集成工具；
实用的线性代数、傅里叶变换、随机数生成等功能
'''
# first_array = np.array([2,3,4])
# print(first_array)
# 多维数组的dtype属性指明了多维数组中元素的类型
# print(first_array.dtype)
# example_list = [2.0, 3.0, 4.0]
# todo 要注意的是，通过这种方式创建数组，经常犯的一个错误是缺少了列表的方括号
# second_array = np.array(example_list)
# print(second_array)

# 列表的亲兄弟元组也可以起到相同的作用
# a = np.array((2,3,4))
# print(a)

# 使用array创建数组时，如果提供的序列对象是嵌套的，NumPy 还可以直接据此生成二维、三维甚至更高维的多维数组
# a = np.array(((2,3,4,5),(4,5,6,7)))
# print(a)

# 创建数组的同时显式指定数据类型
# complex_array = np.array([[1,2],[3,4]], dtype='complex')
# print(complex_array)
# float_array = np.array([[1,2],[3,4]], dtype='float64')
# print(float_array)

# todo 元素未知的创建方式
'''
一般来说，很多时候我们都是知道多维数组的大小，但不知道其元素具体的值，因此 NumPy 提供了一些函数，
可以创建以占位符初始化的固定大小的多维数组。其中，zeros创建的是全为 0 的多维数组，ones创建的时候全为 1 的多维数组，
而empty创建的则是随机初始值的多维数组，数组大小由一个序列（即列表或元组，建议使用元组）参数给定。并且这几种方式的默认类型都是flloat64
'''
# a = np.zeros((3,4))
# b = np.ones((3,4))
# c = np.empty((3, 6)) # empty 的结果就是一块没有初始化的内存。这里由于形状相同，是直接取了上一个数组的值
# print(c)

# todo 等差序列的数组 NumPy 还提供了一个类似于 Python 内置的range的函数arange，用以创建一个由等差序列组成的数组
# a =np.arange(10,30,5)
# a = np.arange( 0, 2, 0.3 )
# a = np.arange(0.2,0.3,0.01)
# print(a)

# todo 元素数量固定的浮点数组
'''
由于浮点精度的原因，使用arange创建浮点数组时，我们不能保证得到我们预期大小的数组，因此这个时候就建议使用linspace这个函数。
linspace与arange的区别就在于它们的第三个参数：前者指定的是最终得到的元素个数，而后者指定的则是元素间的步长
'''
# a = np.linspace(0.2,0.3,9)
# print(a)

# 多维数组的基本属性
'''
多维数组是 NumPy 中最主要的对象。其实就是一个由同种元素组成的元素表，可以由元组进行索引。在 NumPy 中，维度又被称作“轴（axe）”。

注意，在继续介绍数组的各种属性之前，我们要区别开“数组的维度”和“数组某个轴上的维度”。

“数组的维度”指的是数组的“轴”数，用维度空间的概念来理解，也就是数组能够在多少个方向上具有坐标。比如一维的线性空间中，
数组就只能在 x 方向上具有坐标；对于二维的平面空间，数组就在 x 和 y 两个方向上具有坐标。

而“数组轴的维度”则是指的在某个特定的方向上，数组可以有几个刻度，或者说“层次”。比如一维数组[0,1,2]就是在 x 方向上具有 3 个层次；
二维数组[[0,1,2],[2,3,4]]则是在 x 方向上具有 2 个层次，每个层次都是一个在 y 方向上的三个层次的一维数组，在 y 方向上具有 3 个层次，
每个层次都是一个在 x 方向上具有两个层次的一维数组
ndim
ndim即“n dimension”的简写。该属性指示的是多维数组的维数，或者说是“轴数”。

shape
字面意思。这个属性指示的是多维数组整体的维度，或者说是多维数组的“形状”。是一个整型元组，每一个元素都对应与相应轴上的维数。对 n 行 m 列的矩阵而言，它的shape就是(n,m)。shape的元素个数等于多维数组的轴数。

size
多维数组中元素的总个数。等于shape中各元素之积。

dtype
dtype实际上是“data type”的简写，意味着它指示的是多维数组中元素的数据类型。

itemsize
多维数组中，每个元素的字节大小。等效于dtype.itemsize。

data
指示的是包含多维数组中元素实际内存的缓冲区。通常用不到。
'''
# 数组的打印形式
'''
数组在打印的时候长得跟嵌套列表差不多，但其排布都要遵循以下规律：

最后一个轴从左往右打印；
次后轴从上往下打印；
剩下的轴都从上往下打印，每部分由一个空行隔开
也就是说，一维数组按行打印，二维数组按矩阵形式打印，三维及更高维数组会打印成矩阵列表
'''
# print(np.zeros((2,))) # 一维数组
# print(np.zeros((2,3))) # 二维数组
# print(np.zeros((2,3,4))) # 三维数组
# print(np.zeros((2,3,4,5))) # 四维数组

# 重整多维数组的大小 reshape方法，可以将多维数组重整为某个大小
# print(np.arange(12))
# print(np.arange(12).reshape(2,6))
# print(np.arange(12).reshape(6,2))

# 并且reshape还允许缺省 1 个参数（用-1占位），它会根据数组元素的总数和提供的其他参数自动求出一个合适的值，从而得到新的大小的数组
# print(np.arange(12).reshape(2,3,2))
# print(np.arange(12).reshape(2,-1,2))

# 多维数组的索引
'''
所谓索引，在 NumPy 中指的是任何用中括号来获取数组元素值的行为。

NumPy 中，索引的方式有很多，这既使得 NumPy 更加强大灵活，也带来了难于辨析的问题
'''
# 最简单的一种索引方式就是单个索引。对于一维数组，我们可以像对 Python 中的序列一样进行索引
# x = np.arange(10)
# print(x)
# # 对于二维和更高维的数组，我们可以在同一个中括号内直接索引
# x.shape = (2,5) # 等同于 x = x.reshape(2,5)
# print(x[1, 3])
# # 当给出的索引少于数组维数（轴数）时，得到的会是一个数组对象
# print(x[0])
# # TODO 而对于返回的这个数组，我们又可以继续索引，因此对于多维数组而言，也可以使用嵌套序列的索引方式，即使用多个中括号（但这种方式比一次索引要更低效，因此不推荐）
# print(x[0][2])

# NumPy 多维数组又可以像列表一样，进行切片（用冒号“:”）
# x = np.arange(10)
# print(x[2:5])
# print(x[1:7:2])
# 数组也可以用另一个数组来索引
# x = np.arange(10,1,-1)
# print(x[np.array([3, 3, 1, 8])])

# 不常用方式
# y = np.arange(35).reshape(5,7)
# # 多个数组来进行索引
# # print(y[np.array([0,2,4]), np.array([0,1,2])])
# # 还可以用布尔数组作为掩码，筛选数组元素
# b = y>20
# print(y[b])

# todo NumPy 中的广播
# NumPy 中，各种运算默认都是“逐元素”进行的。最简单的例子就是两个明显大小相同的数组运算
# a = np.array([1.0, 2.0, 3.0])
# b = np.array([2.0, 2.0, 2.0])
# print(a*b)

# 逐元素计算
# multi_array = np.array([1.0,2.0,3.0])
# scalar = 2.0
# 标量scalar就好像被扩展为了一个跟multi_array大小相当的数组一样
# print(multi_array*scalar)

# 所谓“相容的形状”，指的是参与运算的这两个数组各个维度要么 1）相等；要么 2）其中一个数组的对应维度为 1（不存在的维度也是 1） 只有当两个数组具有“相容的形状”时，“广播”才能起作用
'''
Image  (3d array): 256 x 256 x 3
Scale  (1d array):             3
Result (3d array): 256 x 256 x 3

A      (4d array):  8 x 1 x 6 x 1
B      (3d array):      7 x 1 x 5
Result (4d array):  8 x 7 x 6 x 5
'''

# 广播的实例
a = np.array([[ 0.0,  0.0,  0.0],
              [10.0, 10.0, 10.0],
              [20.0, 20.0, 20.0],
              [30.0, 30.0, 30.0]])
b = np.array([1.0, 2.0, 3.0])
# print(a.shape)
# print(b.shape)
# 维度不相容是万万不行的
# print(a+b)

# 多维数组切片之图像裁剪
import matplotlib.pyplot as plt
image = plt.imread("python-logo.png")
print(image.shape)
# 取下一半图像
# image_crop = image[300:,::,::]
# plt.imshow(image_crop)
# plt.show()

# 取右边一半的图像：
# image_crop = image[:,400:,:]
# plt.imshow(image_crop)
# plt.show()